/*
 Copyright © 2021-2023  TokiNoBug
This file is part of SlopeCraft.

    SlopeCraft is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    SlopeCraft is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SlopeCraft. If not, see <https://www.gnu.org/licenses/>.

    Contact with me:
    github:https://github.com/SlopeCraft/SlopeCraft
    bilibili:https://space.bilibili.com/351429231
*/

#include "TokiVC.h"
#include "VCL_internal.h"
#include "VisualCraftL.h"
#include <magic_enum.hpp>
#include <map>

const VCL_block *
find_first_mark_block(const std::unordered_map<const VCL_block *, uint16_t>
                          &blocks_allowed) noexcept {
  const VCL_block *p = nullptr;
  for (const auto &pair : blocks_allowed) {
    if (pair.first->is_air()) {
      continue;
    }
    if (pair.first->is_disabled()) {
      continue;
    }

    if (VCL_compare_block(p, pair.first)) {
      p = pair.first;
    }
  }
  return p;
}

bool TokiVC::export_test_litematic_no_lock(const char *filename) noexcept {

  if (!TokiVC_internal::is_basic_color_set_ready ||
      !TokiVC_internal::is_allowed_color_set_ready) {
    VCL_report(
        VCL_report_type_t::error,
        "Trying to export testing litematic before allowed blocks are set.");
    return false;
  }

  libSchem::Schem schem;
  schem.set_MC_major_version_number(TokiVC::version);
  schem.set_MC_version_number(
      MCDataVersion::suggested_version(TokiVC::version));

  // setup block id
  {
    std::vector<const char *> blk_id(TokiVC::blocks_allowed.size());

    for (auto &charp : blk_id) {
      charp = nullptr;
    }

    for (const auto &pair : TokiVC::blocks_allowed) {
      blk_id[pair.second] = pair.first->id_for_schem(TokiVC::version).c_str();
    }

    schem.set_block_id(blk_id.data(), blk_id.size());
  }

  // divide blocks by class
  std::map<
      VCL_block_class_t,
      std::vector<typename decltype(TokiVC::blocks_allowed)::const_iterator>>
      blk_class;

  for (auto cls : magic_enum::enum_values<VCL_block_class_t>()) {
    blk_class[cls] = {};
    blk_class[cls].reserve(TokiVC::blocks_allowed.size() /
                           magic_enum::enum_values<VCL_block_class_t>().size());
  }

  for (auto it = TokiVC::blocks_allowed.begin();
       it != TokiVC::blocks_allowed.end(); ++it) {
    blk_class.at(it->first->block_class).emplace_back(it);
  }

  size_t max_cols = 0;

  for (auto &pair : blk_class) {
    std::sort(pair.second.begin(), pair.second.end(),
              [](typename decltype(TokiVC::blocks_allowed)::const_iterator A,
                 typename decltype(TokiVC::blocks_allowed)::const_iterator B)
                  -> bool { return A->second < B->second; });
    max_cols = std::max(max_cols, pair.second.size());
  }
  max_cols++;

  auto block_class_arr = magic_enum::enum_values<VCL_block_class_t>();
  std::sort(block_class_arr.begin(), block_class_arr.end());

  const size_t rows = block_class_arr.size();

  const int64_t x_range = max_cols;
  const int64_t z_range = rows;
  const int64_t y_range = 2;

  schem.resize(x_range, y_range, z_range);
  schem.fill(0);

  const VCL_block *const marker = find_first_mark_block(TokiVC::blocks_allowed);

  for (size_t idx_class = 0; idx_class < block_class_arr.size(); idx_class++) {
    const auto &vec = blk_class.at(block_class_arr[idx_class]);
    const int64_t z_pos = idx_class;
    size_t idx = 0;
    for (; idx < vec.size(); idx++) {
      const int64_t x_pos = idx;

      schem(x_pos, 0, z_pos) = vec[idx]->second;
    }

    if (marker != nullptr) {
      schem(vec.size(), 1, z_pos) = TokiVC::blocks_allowed.at(marker);
    }
  }

  // the schem is filled.

  // export
  {
    libSchem::litematic_info info;
    info.litename_utf8 =
        fmt::format("Testing litematic for 1.{}", int(TokiVC::version));
    info.author_utf8 = "VisualCraftL";
    info.destricption_utf8 = "This litematic is generated by VisualCraft.";

    return schem.export_litematic(filename, info);
  }
}
